package com.xqkj.commons.virtualdb.templates.impl;

import com.xqkj.commons.constant.HandleResultCode;
import com.xqkj.commons.model.HandleResult;
import com.xqkj.commons.model.basicdo.BasicDo;
import com.xqkj.commons.virtualdb.model.ParametersWrappedModel;
import com.xqkj.commons.virtualdb.model.VirtualDbLockInfoModel;
import com.xqkj.commons.virtualdb.model.VirtualDbLockResultModel;

import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;

/**
 * @author lwt<br>
 * @description <br>
 * @date 2020/1/16
 */
public class OperateModelToVirtualDbHelper<P extends BasicDo> {


    /**
     *
     */
    private Function<VirtualDbLockInfoModel, HandleResult<P>> dbModelGetter;
    /**
     *
     */
    private Function<VirtualDbLockInfoModel, VirtualDbLockResultModel> lockAllFunction;
    /**
     *
     */
    private Consumer<VirtualDbLockInfoModel> unLockAllFunction;


    /**
     *
     * @param parametersWrapped
     * @param virtualDbLockInfoModel
     * @return
     */
    protected <R> HandleResult<R> lockAddMappingAndExecuteOperation(
            ParametersWrappedModel<P> parametersWrapped, VirtualDbLockInfoModel virtualDbLockInfoModel,
            BiFunction<ParametersWrappedModel<P>,VirtualDbLockInfoModel,HandleResult<String>> handleUnionTmpMappingFunction,
            Function<ParametersWrappedModel<P>,HandleResult<R>> operationFunction){
        try {
            //2.尝试锁住所有的唯一性索引 返回null表示没有任何唯一约束
            VirtualDbLockResultModel lockResult = lockAllFunction.apply(virtualDbLockInfoModel);
            if (lockResult == null || !lockResult.isLocked()) {
                lockResult = resetLockResult(lockResult);
                return HandleResult.failed(lockResult.getResultCode(), lockResult.getResultMsg());
            }
            //3.尝试使用唯一性索引获取db中是否已经存在相关数据，如果存在没有异常返回成功--如果数据不存在，entry为null
            HandleResult<P> getDbModelResult = dbModelGetter.apply(virtualDbLockInfoModel);
            if (!getDbModelResult.isSuccess()) {
                return HandleResult.failed(getDbModelResult.getCode(), getDbModelResult.getMsg());
            }
            if (getDbModelResult.getEntry() != null) {
                return HandleResult.failed(HandleResultCode.DATA_IDX_EXISTED.getCode(), getDbModelResult.getMsg());
            }
            //4.处理临时映射--写入数据：直接写入映射；修改唯一索引相关：先删除之前的映射，再写入新的临时映射
            HandleResult<String> mappingHandleResult=handleUnionTmpMappingFunction.apply(parametersWrapped,virtualDbLockInfoModel);
            if(!mappingHandleResult.isSuccess()){
                return HandleResult.failed(mappingHandleResult.getCode(),mappingHandleResult.getMsg());
            }
            //5.将数据写入相关数据表中--事务性写入
            return operationFunction.apply(parametersWrapped);
        } finally {
            unLockAllFunction.accept(virtualDbLockInfoModel);
        }
    }

    public Function<VirtualDbLockInfoModel, HandleResult<P>> getDbModelGetter() {
        return dbModelGetter;
    }

    public void setDbModelGetter(Function<VirtualDbLockInfoModel, HandleResult<P>> dbModelGetter) {
        this.dbModelGetter = dbModelGetter;
    }

    public Function<VirtualDbLockInfoModel, VirtualDbLockResultModel> getLockAllFunction() {
        return lockAllFunction;
    }

    public void setLockAllFunction(Function<VirtualDbLockInfoModel, VirtualDbLockResultModel> lockAllFunction) {
        this.lockAllFunction = lockAllFunction;
    }

    public Consumer<VirtualDbLockInfoModel> getUnLockAllFunction() {
        return unLockAllFunction;
    }

    public void setUnLockAllFunction(Consumer<VirtualDbLockInfoModel> unLockAllFunction) {
        this.unLockAllFunction = unLockAllFunction;
    }

    /**
     *
     * @param lockResult
     * @return
     */
    private VirtualDbLockResultModel resetLockResult(VirtualDbLockResultModel lockResult) {
        if (lockResult == null) {
            lockResult = new VirtualDbLockResultModel();
        }
        if (lockResult.getResultCode() == null) {
            lockResult.setResultCode(HandleResultCode.FAILED.getCode());
        }
        if (lockResult.getResultMsg() == null) {
            lockResult.setResultMsg(HandleResultCode.FAILED.getDesc());
        }
        return lockResult;
    }
}
